package com.bestv.search.management.datasyn;

import com.bestv.search.common.cache.SystemCache;
import com.bestv.search.common.model.SearchKeyword;
import com.bestv.search.common.model.SearchKeywordHistory;
import com.bestv.search.common.model.UserAccessDetail;
import com.bestv.search.management.command.*;
import com.bestv.search.management.xfire.ISearchWS;
import org.apache.log4j.Logger;
import org.codehaus.xfire.client.XFireProxyFactory;
import org.codehaus.xfire.service.Service;
import org.codehaus.xfire.service.binding.ObjectServiceFactory;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.util.StringUtils;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author Michael Wang
 */
public class TopSync extends QuartzJobBean {

    static boolean isRunning = false;
    Logger logger = Logger.getLogger(TopSync.class);
    private TopKeywordCommand topCommand;
    private SearchKeywordHistoryCommand searchKeywordHistoryCommand;
    private UserAccessDetailCommand userAccessDetailCommand;
    private UserStatusCommand userStatusCommand;
    private TopNCacheCommand topNCacheCommand;
    private SystemCache systemCache;
    private String logDir;

    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        if (!isRunning) {
            isRunning = true;
            run();
        } else {
            logger.error("Last sync process is still running");
        }
    }

    void run() {
        try {
            logger.info("Start top sync @" + new Date());

            logger.info("Updating top keywords...");
            topCommand.calcAndStoreTopKeyword(systemCache.getLocalDataSourceKey());

            logger.info("Writing today's accesses to file...");
            List<SearchKeyword> searchKeywords = topCommand.getTimeSortedKeywords(systemCache.getLocalDataSourceKey());
            Map<String, List<SearchKeyword>> formattedKeywordMap = format(searchKeywords);
            for (String date : formattedKeywordMap.keySet()) {
                String filePath = searchKeywordDb2File(date, formattedKeywordMap);

                // Writes search history to DB
                logger.info("Getting search keyword history from file " + filePath);
                SearchKeywordHistory[] historys = searchKeywordHistoryCommand.getSearchKeywordHistoryList(filePath,
                        systemCache.getLocalDataSourceKey());

                logger.info("Save search keyword history to DB");
                searchKeywordHistoryCommand.saveSearchKeywordHistorys(historys, systemCache.getLocalDataSourceKey());

                // Write user access details into DB
                logger.info("Getting user access details from file " + filePath);
                List<UserAccessDetail> accessDetails = userAccessDetailCommand.getUserAccessDetails(filePath);

                logger.info("Save user access details to DB");
                userAccessDetailCommand.saveUserAccessDetailList(accessDetails, systemCache.getLocalDataSourceKey());
            }

            logger.info("Clearing the search keywords...");
            topCommand.clearSearchKeywords(systemCache.getLocalDataSourceKey());

            logger.info("Updates topn cache to DB for keywords and infos");
            topNCacheCommand.updateTopNCache(systemCache.getLocalDataSourceKey());
            updateEngineTopMemoryCache();

            // Update local site's chart
            logger.info("Update all site's keyword charts.");
            searchKeywordHistoryCommand.clearChartsForTopNKeyword(systemCache.getLocalDataSourceKey());
            searchKeywordHistoryCommand.buildChartsForTopNKeyword(systemCache.getLocalDataSourceKey());
            if (systemCache.getMgmtDataSourceKeyList() != null && systemCache.getMgmtDataSourceKeyList().size() > 0) {
                for (String siteCode : systemCache.getMgmtDataSourceKeyList()) {
                    if (!siteCode.equalsIgnoreCase(systemCache.getLocalDataSourceKey())) {
                        searchKeywordHistoryCommand.clearChartsForTopNKeyword(siteCode);
                        searchKeywordHistoryCommand.buildChartsForTopNKeyword(siteCode);
                    }
                }
            }

            logger.info("Update all user status.");
            userStatusCommand.updateUserStatus(systemCache.getLocalDataSourceKey());

            logger.info("End top sync @" + new Date());
        } finally {
            // Always set isRunning = false
            isRunning = false;
        }
    }

    private String searchKeywordDb2File(String date, Map<String, List<SearchKeyword>> keywordMap) {
        String fileDes = null;
        try {
            File accessDir = new File(logDir + "/" + date.substring(0, 6));
            if (!accessDir.exists())
                accessDir.mkdirs();
            fileDes = logDir + "/" + date.substring(0, 6) + "/" + date.substring(6) + ".csv";
            DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            File accessFile = new File(fileDes);
            if (!accessFile.exists())
                accessFile.createNewFile();
            logger.debug("Writing file " + accessFile);
            BufferedWriter bw = new BufferedWriter(new FileWriter(accessFile, true));
            List<SearchKeyword> keywordList = keywordMap.get(date);
            for (SearchKeyword searchKeyword : keywordList) {
                bw.append(dateFormat.format(searchKeyword.getHitTime())).append(",").append(searchKeyword.getKeyword())
                        .append(",").append(searchKeyword.getUserId()).append(",").append(searchKeyword.getDomain())
                        .append(",").append(searchKeyword.getMediaCode()).append("\n");
            }
            bw.flush();
            bw.close();
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("Access file write error!");
        }
        return fileDes;
    }

    private Map<String, List<SearchKeyword>> format(List<SearchKeyword> searchKeywords) {
        String date = "", lastDate = "";
        Map<String, List<SearchKeyword>> result = new HashMap<String, List<SearchKeyword>>();
        List<SearchKeyword> thisRoundList = new ArrayList<SearchKeyword>();
        DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
        for (SearchKeyword searchKeyword : searchKeywords) {
            date = dateFormat.format(searchKeyword.getHitTime());
            if (!date.equals(lastDate)) {
                if (thisRoundList.size() > 0) {
                    result.put(lastDate, thisRoundList);
                }
                thisRoundList = new ArrayList<SearchKeyword>();
                lastDate = date;
            }
            thisRoundList.add(searchKeyword);
        }
        if (thisRoundList.size() > 0)
            result.put(lastDate, thisRoundList);
        return result;
    }

    private void updateEngineTopMemoryCache() {
        ISearchWS service;
        Service serviceModel = new ObjectServiceFactory().create(ISearchWS.class);
        String url = systemCache.getWebServiceEngineUrls().get(systemCache.getLocalDataSourceKey());
        String[] soapUrls = StringUtils.tokenizeToStringArray(url, SystemCache.WEB_SERVICE_EURL_DELIMITERS);
        if (soapUrls == null || soapUrls.length == 0) {
            logger.error("UpdateEngineTopMemoryCache failed, no web service interface found from site "
                    + systemCache.getLocalDataSourceKey());
            return;
        }

        try {
            for (int i = 0; i < soapUrls.length; i++) {
                String soapUrl = soapUrls[i];
                logger.debug("UpdateEngineTopMemoryCache from " + soapUrl);

                service = (ISearchWS) new XFireProxyFactory().create(serviceModel, soapUrl);
                service.updateTopNCacheInMemory();

                logger.debug("UpdateEngineTopMemoryCache end from " + soapUrl);
            }
        } catch (Exception e) {
            logger.error("updateEngineTopMemoryCache failed due to ");
            e.printStackTrace();
        }
    }

    public TopKeywordCommand getTopCommand() {
        return topCommand;
    }

    public void setTopCommand(TopKeywordCommand topCommand) {
        this.topCommand = topCommand;
    }

    public String getLogDir() {
        return logDir;
    }

    public void setLogDir(String logDir) {
        this.logDir = logDir;
    }

    public SearchKeywordHistoryCommand getSearchKeywordHistoryCommand() {
        return searchKeywordHistoryCommand;
    }

    public void setSearchKeywordHistoryCommand(SearchKeywordHistoryCommand searchKeywordHistoryCommand) {
        this.searchKeywordHistoryCommand = searchKeywordHistoryCommand;
    }

    public UserAccessDetailCommand getUserAccessDetailCommand() {
        return userAccessDetailCommand;
    }

    public void setUserAccessDetailCommand(UserAccessDetailCommand userAccessDetailCommand) {
        this.userAccessDetailCommand = userAccessDetailCommand;
    }

    public UserStatusCommand getUserStatusCommand() {
        return userStatusCommand;
    }

    public void setUserStatusCommand(UserStatusCommand userStatusCommand) {
        this.userStatusCommand = userStatusCommand;
    }

    public SystemCache getSystemCache() {
        return systemCache;
    }

    public void setSystemCache(SystemCache systemCache) {
        this.systemCache = systemCache;
    }

    public TopNCacheCommand getTopNCacheCommand() {
        return topNCacheCommand;
    }

    public void setTopNCacheCommand(TopNCacheCommand topNCacheCommand) {
        this.topNCacheCommand = topNCacheCommand;
    }
}
